package com.gitee.hermer.boot.jee.commons.reflect;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;

import com.gitee.hermer.boot.jee.commons.exception.ErrorCode;
import com.gitee.hermer.boot.jee.commons.exception.PaiUException;
import com.gitee.hermer.boot.jee.commons.utils.CodeLocationUtils;
import com.gitee.hermer.boot.jee.commons.verify.Assert;


import sun.misc.Unsafe;


public class ReflectUtils {


	private static final Map primitiveValueMap = new HashMap(16);
	// 调用该方法用于生成method.invoke需要的实际执行者
	protected static Method acquireMethodAccessor;

	// 该属性是method.invoke的实际执行者
	protected static Field methodAccessor;

	// 用于执行低级别、不安全操作的方法集合
	private static Unsafe unsafe;

	static {
		primitiveValueMap.put(Boolean.class, Boolean.FALSE);
		primitiveValueMap.put(Byte.class, Byte.valueOf((byte) 0));
		primitiveValueMap.put(Character.class, Character.valueOf((char) 0));
		primitiveValueMap.put(Short.class, Short.valueOf((short) 0));
		primitiveValueMap.put(Double.class, Double.valueOf(0));
		primitiveValueMap.put(Float.class, Float.valueOf(0));
		primitiveValueMap.put(Integer.class, Integer.valueOf(0));
		primitiveValueMap.put(Long.class, Long.valueOf(0));
		primitiveValueMap.put(boolean.class, Boolean.FALSE);
		primitiveValueMap.put(byte.class, Byte.valueOf((byte) 0));
		primitiveValueMap.put(char.class, Character.valueOf((char) 0));
		primitiveValueMap.put(short.class, Short.valueOf((short) 0));
		primitiveValueMap.put(double.class, Double.valueOf(0));
		primitiveValueMap.put(float.class, Float.valueOf(0));
		primitiveValueMap.put(int.class, Integer.valueOf(0));
		primitiveValueMap.put(long.class, Long.valueOf(0));

		try {
			// 由反编译Unsafe类获得的信息
			Field field = Unsafe.class.getDeclaredField("theUnsafe");
			field.setAccessible(true);
			// 获取静态属性,Unsafe在启动JVM时随rt.jar装载
			unsafe = (Unsafe) field.get(null);
			acquireMethodAccessor = Method.class.getDeclaredMethod("acquireMethodAccessor");
			acquireMethodAccessor.setAccessible(true);
			methodAccessor = Method.class.getDeclaredField("methodAccessor");
			methodAccessor.setAccessible(true);

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static Unsafe getUnsafe() {
		return unsafe;
	}

	/**
	 * 获取字段的偏移量
	 * @param fieldName
	 * @param type
	 * @return
	 * @throws PaiUException 
	 */
	public static long getFieldOffset(String fieldName, Class<?> type) throws PaiUException {
		try {
			Field field = type.getDeclaredField(fieldName);
			field.setAccessible(true);
			Assert.isTrueCode(!Modifier.isStatic(field.getModifiers()),ErrorCode.SYSTEM_ERROR,String.format("属性%s.%s是静态属性,不应该使用该方法,请检查%s", field.getDeclaringClass(), field.getName(), CodeLocationUtils.getCodeLocation(2)));
			return unsafe.objectFieldOffset(field);
		} catch (NoSuchFieldException | SecurityException e) {
			throw new RuntimeException(e);
		}
	}

	public static Object newInstance(Class type) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Constructor constructor = null;
		Object[] constructorArgs = new Object[0];
		try {
			constructor = type.getConstructor(new Class[] {});
		} catch (NoSuchMethodException e) {
			// ignore

		}

		if (constructor == null) {
			Constructor[] constructors = type.getConstructors();
			if (constructors == null || constructors.length == 0) {
				throw new UnsupportedOperationException("Class[" + type.getName() + "] has no public constructors");
			}
			constructor = constructors[getSimpleParamenterTypeIndex(constructors)];
			Class[] params = constructor.getParameterTypes();
			constructorArgs = new Object[params.length];
			for (int i = 0; i < params.length; i++) {
				constructorArgs[i] = getDefaultValue(params[i]);
			}
		}
		return constructor.newInstance(constructorArgs);
	}

	public static int getSimpleParamenterTypeIndex(Constructor[] constructors) {
		Constructor constructor = null;
		Class[] params = null;
		boolean isSimpleTypes;
		for (int i = 0; i < constructors.length; i++) {
			constructor = constructors[i];
			params = constructor.getParameterTypes();
			if (params.length > 0) {
				isSimpleTypes = true;
				for (int j = 0; j < params.length; j++) {
					if (primitiveValueMap.get(params[j]) == null) {
						isSimpleTypes = false;
						break;
					}
				}
				if (isSimpleTypes) {
					return i;
				}
			} else {
				return i;
			}
		}
		return 0;
	}

	public static Object getDefaultValue(Class cl) {
		if (cl.isArray()) {
			return Array.newInstance(cl.getComponentType(), 0);
		} else if (cl.isPrimitive() || primitiveValueMap.containsKey(cl)) {
			return primitiveValueMap.get(cl);
		} else {
			return null;
		}
	}

	public static Object invoke(Object finObj, Object[] finArgs, Method method) throws Throwable {
		try {
			return method.invoke(finObj, finArgs);
		} catch (IllegalAccessException e) {
			throw e;
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (InvocationTargetException e) {
			throw e.getTargetException() != null ? e.getTargetException() : e;
		}
	}

	public static Class<?> getGenericClass(ParameterizedType parameterizedType, int i) {
		Object genericClass = parameterizedType.getActualTypeArguments()[i];
		if (genericClass instanceof ParameterizedType) {
			return (Class<?>) ((ParameterizedType) genericClass).getRawType();
		} else if (genericClass instanceof GenericArrayType) {
			return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
		} else {
			return (Class<?>) genericClass;
		}
	}
}
